20. Pass by Value & Reference

Pass by Value
C++은 기본적으로 값에 의한 전달(pass-by-value) 방식을 사용한다.
값으로 전달할 때, 사본을 통해 초기화 되며, 반환할 때도 사본을 돌려받게 된다.
(복사생성자를 이용해서 사본을 생성)
class Person{
public:
Person();
virtual ~Person();
// ...
private:
std::string name;
std::string address;
};
class Student: public Person{
public:
Student();
~Student();
// ...
private:
std::string schoolName;
std::string schoolAddress;
};
bool validateStudent(Student s); // Student
Student plato;
bool platoIsOK=validateStudent(plato);
plato를 매개변수로 전달하게되면, s를 초기화시키기 위해 Student의 복사 생성자를 호출한다.
이 후, s는 validateStudent가 bool를 리턴할 때, s는 소멸됨
(복사 생성자 1번 호출, 소멸자 1번 호출)
추가적으로 plato 내부에 선언되어 있는 string 객체또한 복사해 주어야 한다.
위와 같이 Person을 상속한 파생 클래스의 경우, 기본 클래스의 생성자를 먼저 호출해 주어야 하고,
기본 클래스의 소멸자도 호출해 주어야 한다.
(총 6번의 생성자와 6번의 소멸자가 호출됨)
이와 같이 값으로 전달함은 고비용 연산이다.
Pass by Reference(reference to const)
bool validateStudent(const Student& s);
레퍼런스로 값을 전달하게 되면, 생성자와 소멸자를 호출할 필요가 없다.
대신 레퍼런스로 전달된 객체는 사본이 아니기 때문에 const로 불변성을 정의해 주어야 한다.

또한 레퍼런스로 전달할 시, 복사손실 문제(slicing problem)를 없앨 수 있다.
class Window{
public:
// ...
std::string name() const;
virtual void display() const;
};
class WindowWithScrollBars: public Window{
public:
// ...
virtual void display() const;
};
void printNameAndDisplay(Window w){ //
std::cout<<w.name();
w.display();
}
WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);
위와 같이 WindowWithScrollBars(파생클래스) 매개변수 위치에 Window(기본클래스)를 값으로 전달하게 되면,
매개변수가 복사손실 당한다. 이 때, WindowWithScrollBars가 가지고 있는 부속 정보를 잃게 된다.
(위세어 w.display()는 Window 클래스의 display가 실행될 것이다.)

참조자로 객체를 전달하면 복사손실을 막을 수 있다.
void printNameAndDisplay(const Window& w){
std::cout<<w.name();
w.display();
}
C++의 참조자는 내부적으로 포인터로 구현되어 있다.
즉, 참조자를 전달하는 것은 결국 포인터를 전달하는 것과 동일하다.
(int 등의 기본타입의 경우, '값으로 인한 전달’과 ‘참조에 의한 전달’이 비슷하다.)

크기가 작은 객체도 컴파일러에 따라서 동일한 크기의 기본제공 타입과 다르게 취급할 수 있다.
포인터로 전달(레퍼런스 전달)은 항상 레지스터에 들어가기 때문에 빠른 속도를 보장한다.

값에 의한 전달이 저비용이라고 할 수 있는 경우는 아래와 같다.
1. 기본제공 타입
2. STL 반복자
3. 함수 객체 타입

이외에는 ‘참조에 의한 전달'을 사용하는 것이 좋다.